home *** CD-ROM | disk | FTP | other *** search
/ mail.altrad.com / 2015.02.mail.altrad.com.tar / mail.altrad.com / TEST / office german / PROPLUS.WW / PROPLSWW.CAB / OMML2MML.XSL < prev    next >
Extensible Markup Language  |  2006-08-27  |  54KB  |  1,312 lines

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:mml="http://www.w3.org/1998/Math/MathML"
  3.     xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">
  4.     <xsl:output method="xml" encoding="UTF-16" />
  5.  
  6.     <!-- %% Global Definitions -->
  7.     
  8.     <!-- Every single unicode character that is recognized by OMML as an operator -->
  9.     <xsl:variable name="sOperators"
  10.         select="concat(
  11.                     '!"()+,-/⫾:;<',
  12.                     '=>?[\]{|}¡¬±',
  13.                     '·¿×÷        ',
  14.                     ' ‐‒–—‖†‡•․‥…',
  15.                     '‼⁀⁎⁏⁐ ⁡⁢⁣⅀←↑',
  16.                     '→↓↔↕↖↗↘↙↚↛↜↝',
  17.                     '↞↟↠↡↢↣↤↥↦↧↨↩',
  18.                     '↪↫↬↭↮↯↰↱↲↳↶↷',
  19.                     '↺↻↼↽↾↿⇀⇁⇂⇃⇄⇅',
  20.                     '⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏⇐⇑',
  21.                     '⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜⇝',
  22.                     '⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩',
  23.                     '⇴⇵⇶⇷⇸⇹⇺⇻⇼⇽⇾⇿',
  24.                     '∀∁∂∃∄∆∇∈∉∊∋∌',
  25.                     '∍∏∐∑−∓∔∕∖∗∘∙',
  26.                     '√∛∜∝∣∤∥∦∧∨∩∪',
  27.                     '∫∬∭∮∯∰∱∲∳∴∵∶',
  28.                     '∷∸∹∺∻∼∽∾≀≁≂≃',
  29.                     '≄≅≆≇≈≉≊≋≌≍≎≏',
  30.                     '≐≑≒≓≔≕≖≗≘≙≚≛',
  31.                     '≜≝≞≟≠≡≢≣≤≥≦≧',
  32.                     '≨≩≪≫≬≭≮≯≰≱≲≳',
  33.                     '≴≵≶≷≸≹≺≻≼≽≾≿',
  34.                     '⊀⊁⊂⊃⊄⊅⊆⊇⊈⊉⊊⊋',
  35.                     '⊌⊍⊎⊏⊐⊑⊒⊓⊔⊕⊖⊗',
  36.                     '⊘⊙⊚⊛⊜⊝⊞⊟⊠⊡⊢⊣',
  37.                     '⊥⊦⊧⊨⊩⊪⊫⊬⊭⊮⊯⊰',
  38.                     '⊱⊲⊳⊴⊵⊶⊷⊸⊹⊺⊻⊼',
  39.                     '⊽⋀⋁⋂⋃⋄⋅⋆⋇⋈⋉⋊',
  40.                     '⋋⋌⋍⋎⋏⋐⋑⋒⋓⋔⋕⋖',
  41.                     '⋗⋘⋙⋚⋛⋜⋝⋞⋟⋠⋡⋢',
  42.                     '⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬⋭⋮',
  43.                     '⋯⋰⋱⋲⋳⋴⋵⋶⋷⋸⋹⋺',
  44.                     '⋻⋼⋽⋾⋿⌅⌆⌈⌉⌊⌋⌜',
  45.                     '⌝⌞⌟⌢⌣〈〉⌽⌿⎰⎱▲',
  46.                     '△▴▵▶▷▸▹▼▽▾▿◀',
  47.                     '◁◂◃◄◅◊○◦◫◬◸◹',
  48.                     '◺◻◼◽◾◿★☆❲❳⟑⟒',
  49.                     '⟓⟔⟕⟖⟗⟘⟙⟚⟛⟜⟝⟞',
  50.                     '⟟⟠⟡⟢⟣⟤⟥⟦⟧⟨⟩⟪',
  51.                     '⟫⟰⟱⟲⟳⟴⟵⟶⟷⟸⟹⟺',
  52.                     '⟻⟼⟽⟾⟿⤀⤁⤂⤃⤄⤅⤆',
  53.                     '⤇⤈⤉⤊⤋⤌⤍⤎⤏⤐⤑⤒',
  54.                     '⤓⤔⤕⤖⤗⤘⤙⤚⤛⤜⤝⤞',
  55.                     '⤟⤠⤡⤢⤣⤤⤥⤦⤧⤨⤩⤪',
  56.                     '⤫⤬⤭⤮⤯⤰⤱⤲⤳⤴⤵⤶',
  57.                     '⤷⤸⤹⤺⤻⤼⤽⤾⤿⥀⥁⥂',
  58.                     '⥃⥄⥅⥆⥇⥈⥉⥊⥋⥌⥍⥎',
  59.                     '⥏⥐⥑⥒⥓⥔⥕⥖⥗⥘⥙⥚',
  60.                     '⥛⥜⥝⥞⥟⥠⥡⥢⥣⥤⥥⥦',
  61.                     '⥧⥨⥩⥪⥫⥬⥭⥮⥯⥰⥱⥲',
  62.                     '⥳⥴⥵⥶⥷⥸⥹⥺⥻⥼⥽⥾',
  63.                     '⥿⦀⦂⦃⦄⦅⦆⦇⦈⦉⦊⦋',
  64.                     '⦌⦍⦎⦏⦐⦑⦒⦓⦔⦕⦖⦗',
  65.                     '⦘⦙⦚⦶⦷⦸⦹⧀⧁⧄⧅⧆',
  66.                     '⧇⧈⧎⧏⧐⧑⧒⧓⧔⧕⧖⧗',
  67.                     '⧘⧙⧚⧛⧟⧡⧢⧣⧤⧥⧦⧫',
  68.                     '⧴⧵⧶⧷⧸⧹⧺⧻⧼⧽⧾⧿',
  69.                     '⨀⨁⨂⨃⨄⨅⨆⨇⨈⨉⨊⨋',
  70.                     '⨌⨍⨎⨏⨐⨑⨒⨓⨔⨕⨖⨗',
  71.                     '⨘⨙⨚⨛⨜⨝⨞⨟⨠⨡⨢⨣',
  72.                     '⨤⨥⨦⨧⨨⨩⨪⨫⨬⨭⨮⨯',
  73.                     '⨰⨱⨲⨳⨴⨵⨶⨷⨸⨹⨺⨻',
  74.                     '⨼⨽⨾⨿⩀⩁⩂⩃⩄⩅⩆⩇',
  75.                     '⩈⩉⩊⩋⩌⩍⩎⩏⩐⩑⩒⩓',
  76.                     '⩔⩕⩖⩗⩘⩙⩚⩛⩜⩝⩞⩟',
  77.                     '⩠⩡⩢⩣⩤⩥⩦⩧⩨⩩⩪⩫',
  78.                     '⩬⩭⩮⩯⩰⩱⩲⩳⩴⩵⩶⩷',
  79.                     '⩸⩹⩺⩻⩼⩽⩾⩿⪀⪁⪂⪃',
  80.                     '⪄⪅⪆⪇⪈⪉⪊⪋⪌⪍⪎⪏',
  81.                     '⪐⪑⪒⪓⪔⪕⪖⪗⪘⪙⪚⪛',
  82.                     '⪜⪝⪞⪟⪠⪡⪢⪣⪤⪥⪦⪧',
  83.                     '⪨⪩⪪⪫⪬⪭⪮⪯⪰⪱⪲⪳',
  84.                     '⪴⪵⪶⪷⪸⪹⪺⪻⪼⪽⪾⪿',
  85.                     '⫀⫁⫂⫃⫄⫅⫆⫇⫈⫉⫊⫋',
  86.                     '⫌⫍⫎⫏⫐⫑⫒⫓⫔⫕⫖⫗',
  87.                     '⫘⫙⫚⫛⫝̸⫝⫞⫟⫠⫢⫣⫤',
  88.                     '⫥⫦⫧⫨⫩⫪⫫⫬⫭⫮⫯⫰',
  89.                     '⫲⫳⫴⫵⫶⫷⫸⫹⫺⫻⫼⫽')" />
  90.                     
  91.     <!-- A string of '-'s repeated exactly as many times as the operators above -->
  92.     <xsl:variable name="sMinuses">
  93.         <xsl:call-template name="SRepeatChar">
  94.             <xsl:with-param name="cchRequired" select="string-length($sOperators)" />
  95.             <xsl:with-param name="ch" select="'-'" />
  96.         </xsl:call-template>
  97.     </xsl:variable>
  98.     
  99.     <!-- Every single unicode character that is recognized by OMML as a number -->
  100.     <xsl:variable name="sNumbers" select="'0123456789'"/>
  101.     
  102.     <!-- A string of '0's repeated exactly as many times as the list of numbers above -->
  103.     <xsl:variable name="sZeros">
  104.         <xsl:call-template name="SRepeatChar">
  105.             <xsl:with-param name="cchRequired" select="string-length($sNumbers)" />
  106.             <xsl:with-param name="ch" select="'0'" />
  107.         </xsl:call-template>
  108.     </xsl:variable>    
  109.  
  110.     <!-- %%Template: SReplace
  111.  
  112.         Replace all occurences of sOrig in sInput with sReplacement
  113.         and return the resulting string. -->
  114.     <xsl:template name="SReplace">
  115.         <xsl:param name="sInput" />
  116.         <xsl:param name="sOrig" />
  117.         <xsl:param name="sReplacement" />
  118.  
  119.         <xsl:choose>
  120.             <xsl:when test="not(contains($sInput, $sOrig))">
  121.                 <xsl:value-of select="$sInput" />
  122.             </xsl:when>
  123.             <xsl:otherwise>
  124.                 <xsl:variable name="sBefore" select="substring-before($sInput, $sOrig)" />
  125.                 <xsl:variable name="sAfter" select="substring-after($sInput, $sOrig)" />
  126.                 <xsl:variable name="sAfterProcessed">
  127.                     <xsl:call-template name="SReplace">
  128.                         <xsl:with-param name="sInput" select="$sAfter" />
  129.                         <xsl:with-param name="sOrig" select="$sOrig" />
  130.                         <xsl:with-param name="sReplacement" select="$sReplacement" />
  131.                     </xsl:call-template>
  132.                 </xsl:variable>
  133.  
  134.                 <xsl:value-of select="concat($sBefore, concat($sReplacement, $sAfterProcessed))" /> 
  135.             </xsl:otherwise>
  136.         </xsl:choose>
  137.     </xsl:template>    
  138.         
  139.     <!-- Templates -->                    
  140.     <xsl:template match="/">
  141.         <mml:math>
  142.             <xsl:apply-templates select="*" />
  143.         </mml:math>
  144.     </xsl:template>
  145.     
  146.     <xsl:template match="*">
  147.         <xsl:apply-templates select="*" />
  148.     </xsl:template>
  149.     
  150.     <xsl:template match="m:acc"> 
  151.         <mml:mover>
  152.             <xsl:attribute name="accent">true</xsl:attribute>
  153.             <xsl:apply-templates select="m:e[1]" /> 
  154.             <mml:mtext>
  155.                 <xsl:call-template name="CreateTokenAttributes">
  156.                     <xsl:with-param name="scr" select="m:e[1]/*/m:rPr[last()]/m:scr/@m:val" />
  157.                     <xsl:with-param name="sty" select="m:e[1]/*/m:rPr[last()]/m:sty/@m:val" />
  158.                     <xsl:with-param name="nor" select="m:e[1]/*/m:rPr[last()]/m:nor/@m:val" />
  159.                 </xsl:call-template>
  160.                 <xsl:choose>
  161.                     <xsl:when test="not(m:accPr[last()]/m:chr)">
  162.                         <xsl:value-of select="'̂'" /> 
  163.                     </xsl:when>
  164.                     <xsl:otherwise>
  165.                         <xsl:value-of select="substring(m:accPr/m:chr/@m:val,1,1)" />
  166.                     </xsl:otherwise>
  167.                 </xsl:choose>
  168.             </mml:mtext>
  169.         </mml:mover>
  170.     </xsl:template>
  171.     
  172.     <xsl:template match="m:sPre">
  173.         <mml:mmultiscripts>
  174.             <mml:mrow>
  175.                 <xsl:apply-templates select="m:e[1]" />
  176.             </mml:mrow>
  177.             <mml:mprescripts />
  178.             <mml:mrow>
  179.                 <xsl:apply-templates select="m:sub[1]" />
  180.             </mml:mrow>
  181.             <mml:mrow>
  182.                 <xsl:apply-templates select="m:sup[1]" />
  183.             </mml:mrow>
  184.         </mml:mmultiscripts>
  185.     </xsl:template>
  186.     
  187.     <xsl:template match="m:m">
  188.         <mml:mtable>
  189.             <xsl:call-template name="CreateMathMLMatrixAttr">
  190.                 <xsl:with-param name="mcJc" select="m:mPr[last()]/m:mcs/m:mc/m:mcPr[last()]/m:mcJc/@m:val" />
  191.             </xsl:call-template>
  192.             <xsl:for-each select="m:mr">
  193.                 <mml:mtr>
  194.                     <xsl:for-each select="m:e">
  195.                         <mml:mtd>
  196.                             <xsl:apply-templates select="." />
  197.                         </mml:mtd>
  198.                     </xsl:for-each>
  199.                 </mml:mtr>
  200.             </xsl:for-each>
  201.         </mml:mtable>
  202.     </xsl:template>
  203.     
  204.     <xsl:template name="CreateMathMLMatrixAttr">
  205.         <xsl:param name="mcJc" />
  206.         <xsl:variable name="sLowerCaseMcjc" select="translate($mcJc, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
  207.                                                                      'abcdefghijklmnopqrstuvwxyz')" />
  208.         <xsl:choose>
  209.             <xsl:when test="$sLowerCaseMcjc='left'">
  210.                 <xsl:attribute name="columnalign">left</xsl:attribute>
  211.             </xsl:when>
  212.             <xsl:when test="$sLowerCaseMcjc='right'">
  213.                 <xsl:attribute name="columnalign">right</xsl:attribute>
  214.             </xsl:when>
  215.         </xsl:choose>
  216.     </xsl:template>
  217.     
  218.     <xsl:template match="m:phant">
  219.         <xsl:variable name="sLowerCaseWidth" select="translate(m:phantPr[last()]/m:width/@m:val, 
  220.                                                                'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
  221.                                                                'abcdefghijklmnopqrstuvwxyz')" />
  222.         <xsl:variable name="sLowerCaseAsc" select="translate(m:phantPr[last()]/m:asc/@m:val, 
  223.                                                              'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
  224.                                                              'abcdefghijklmnopqrstuvwxyz')" />
  225.         <xsl:variable name="sLowerCaseDec" select="translate(m:phantPr[last()]/m:dec/@m:val, 
  226.                                                              'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
  227.                                                              'abcdefghijklmnopqrstuvwxyz')" />
  228.         <xsl:if test="not($sLowerCaseWidth='off' and 
  229.                           $sLowerCaseAsc='off'   and
  230.                           $sLowerCaseDec='off')">
  231.             <mml:phantom>                
  232.                 <xsl:apply-templates select="m:e[1]" />
  233.             </mml:phantom>
  234.         </xsl:if>
  235.     </xsl:template>
  236.     
  237.     <xsl:template match="m:rad"> 
  238.         <xsl:variable name="sLowerCaseDegHide" select="translate(m:radPr[last()]/m:degHide/@m:val, 
  239.                                                                   'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
  240.                                                                   'abcdefghijklmnopqrstuvwxyz')" />
  241.         <xsl:choose>
  242.             <xsl:when test="$sLowerCaseDegHide='on'"> 
  243.                 <mml:msqrt>
  244.                     <xsl:apply-templates select="m:e[1]" />
  245.                 </mml:msqrt>
  246.             </xsl:when>
  247.             <xsl:otherwise> 
  248.                 <mml:mroot>
  249.                     <mml:mrow>
  250.                         <xsl:apply-templates select="m:e[1]" />
  251.                     </mml:mrow>
  252.                     <mml:mrow>
  253.                         <xsl:apply-templates select="m:deg[1]" />
  254.                     </mml:mrow>
  255.                 </mml:mroot>
  256.             </xsl:otherwise>
  257.         </xsl:choose>
  258.     </xsl:template>
  259.     
  260.     <!-- %%Template match m:nary 
  261.         Process an n-ary. 
  262.         
  263.         Decides, based on which arguments are supplied, between
  264.         using an mo, msup, msub, or msubsup for the n-ary operator        
  265.     -->
  266.     <xsl:template match="m:nary">
  267.         <xsl:variable name="sLowerCaseSubHide">
  268.             <xsl:choose>
  269.                 <xsl:when test="count(m:naryPr[last()]/m:subHide) = 0">
  270.                     <xsl:text>off</xsl:text>
  271.                 </xsl:when>
  272.                 <xsl:otherwise>
  273.                     <xsl:value-of select="translate(m:naryPr[last()]/m:subHide/@m:val, 
  274.                                       'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
  275.                                       'abcdefghijklmnopqrstuvwxyz')" />
  276.                 </xsl:otherwise>
  277.             </xsl:choose>
  278.         </xsl:variable>
  279.         <xsl:variable name="sLowerCaseSupHide">
  280.             <xsl:choose>
  281.                 <xsl:when test="count(m:naryPr[last()]/m:supHide) = 0">
  282.                     <xsl:text>off</xsl:text>
  283.                 </xsl:when>
  284.                 <xsl:otherwise>
  285.                     <xsl:value-of select="translate(m:naryPr[last()]/m:supHide/@m:val, 
  286.                                       'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
  287.                                       'abcdefghijklmnopqrstuvwxyz')" />
  288.                 </xsl:otherwise>
  289.             </xsl:choose>
  290.         </xsl:variable>
  291.         <xsl:choose>
  292.             <xsl:when test="not($sLowerCaseSupHide='off') and 
  293.                             not($sLowerCaseSubHide='off')">
  294.                 <mml:mo>
  295.                     <xsl:choose>
  296.                         <xsl:when test="not(m:naryPr[last()]/m:chr/@m:val) or
  297.                                         m:naryPr[last()]/m:chr/@m:val=''">
  298.                             <xsl:text disable-output-escaping="yes">&#x222b;</xsl:text>
  299.                         </xsl:when>
  300.                         <xsl:otherwise>
  301.                             <xsl:value-of select="m:naryPr[last()]/m:chr/@m:val" />
  302.                         </xsl:otherwise>
  303.                     </xsl:choose>
  304.                 </mml:mo>
  305.             </xsl:when>
  306.             <xsl:when test="not($sLowerCaseSubHide='off')">
  307.                 <xsl:choose>
  308.                     <xsl:when test="m:naryPr[last()]/m:limLoc/@m:val='subSup'">
  309.                         <mml:msup>
  310.                             <mml:mo>
  311.                                 <xsl:choose>
  312.                                     <xsl:when test="not(m:naryPr[last()]/m:chr/@m:val) or
  313.                                                     m:naryPr[last()]/m:chr/@m:val=''">
  314.                                         <xsl:text disable-output-escaping="yes">&#x222b;</xsl:text>
  315.                                     </xsl:when>
  316.                                     <xsl:otherwise>
  317.                                         <xsl:value-of select="m:naryPr[last()]/m:chr/@m:val" />
  318.                                     </xsl:otherwise>
  319.                                 </xsl:choose>
  320.                             </mml:mo>
  321.                             <xsl:apply-templates select="m:sup[1]" />
  322.                         </mml:msup>
  323.                     </xsl:when>
  324.                     <xsl:otherwise>
  325.                         <mml:mover>
  326.                             <mml:mo>
  327.                                 <xsl:choose>
  328.                                     <xsl:when test="not(m:naryPr[last()]/m:chr/@m:val) or
  329.                                                     m:naryPr[last()]/m:chr/@m:val=''">
  330.                                         <xsl:text disable-output-escaping="yes">&#x222b;</xsl:text>
  331.                                     </xsl:when>
  332.                                     <xsl:otherwise>
  333.                                         <xsl:value-of select="m:naryPr[last()]/m:chr/@m:val" />
  334.                                     </xsl:otherwise>
  335.                                 </xsl:choose>
  336.                             </mml:mo>
  337.                             <xsl:apply-templates select="m:sup[1]" />
  338.                         </mml:mover>
  339.                     </xsl:otherwise>
  340.                 </xsl:choose>
  341.             </xsl:when>
  342.             <xsl:when test="not($sLowerCaseSupHide='off')">
  343.                 <xsl:choose>
  344.                     <xsl:when test="m:naryPr[last()]/m:limLoc/@m:val='subSup'">
  345.                         <mml:msub>
  346.                             <mml:mo>
  347.                                 <xsl:choose>
  348.                                     <xsl:when test="not(m:naryPr[last()]/m:chr/@m:val) or
  349.                                                     m:naryPr[last()]/m:chr/@m:val=''">
  350.                                         <xsl:text disable-output-escaping="yes">&#x222b;</xsl:text>
  351.                                     </xsl:when>
  352.                                     <xsl:otherwise>
  353.                                         <xsl:value-of select="m:naryPr[last()]/m:chr/@m:val" />
  354.                                     </xsl:otherwise>
  355.                                 </xsl:choose>
  356.                             </mml:mo>
  357.                             <xsl:apply-templates select="m:sub[1]" />
  358.                         </mml:msub>
  359.                     </xsl:when>
  360.                     <xsl:otherwise>
  361.                         <mml:munder>
  362.                             <mml:mo>
  363.                                 <xsl:choose>
  364.                                     <xsl:when test="not(m:naryPr[last()]/m:chr/@m:val) or
  365.                                     m:naryPr[last()]/m:chr/@m:val=''">
  366.                                         <xsl:text disable-output-escaping="yes">&#x222b;</xsl:text>
  367.                                     </xsl:when>
  368.                                     <xsl:otherwise>
  369.                                         <xsl:value-of select="m:naryPr[last()]/m:chr/@m:val" />
  370.                                     </xsl:otherwise>
  371.                                 </xsl:choose>
  372.                             </mml:mo>
  373.                             <xsl:apply-templates select="m:sub[1]" />
  374.                         </mml:munder>
  375.                     </xsl:otherwise>
  376.                 </xsl:choose>
  377.             </xsl:when>
  378.             <xsl:otherwise>
  379.                 <xsl:choose>
  380.                     <xsl:when test="m:naryPr[last()]/m:limLoc/@m:val='subSup'">
  381.                         <mml:msubsup>
  382.                             <mml:mo>
  383.                                 <xsl:choose>
  384.                                     <xsl:when test="not(m:naryPr[last()]/m:chr/@m:val) or
  385.                                                     m:naryPr[last()]/m:chr/@m:val=''">
  386.                                         <xsl:text disable-output-escaping="yes">&#x222b;</xsl:text>
  387.                                     </xsl:when>
  388.                                     <xsl:otherwise>
  389.                                         <xsl:value-of select="m:naryPr[last()]/m:chr/@m:val" />
  390.                                     </xsl:otherwise>
  391.                                 </xsl:choose>
  392.                             </mml:mo>
  393.                             <xsl:apply-templates select="m:sub[1]" />
  394.                             <xsl:apply-templates select="m:sup[1]" />
  395.                         </mml:msubsup>
  396.                     </xsl:when>
  397.                     <xsl:otherwise>
  398.                         <mml:munderover>
  399.                             <mml:mo>
  400.                                 <xsl:choose>
  401.                                     <xsl:when test="not(m:naryPr[last()]/m:chr/@m:val) or
  402.                                                     m:naryPr[last()]/m:chr/@m:val=''">
  403.                                         <xsl:text disable-output-escaping="yes">&#x222b;</xsl:text>
  404.                                     </xsl:when>
  405.                                     <xsl:otherwise>
  406.                                         <xsl:value-of select="m:naryPr[last()]/m:chr/@m:val" />
  407.                                     </xsl:otherwise>
  408.                                 </xsl:choose>
  409.                             </mml:mo>
  410.                             <xsl:apply-templates select="m:sub[1]" />
  411.                             <xsl:apply-templates select="m:sup[1]" />
  412.                         </mml:munderover>
  413.                     </xsl:otherwise>
  414.                 </xsl:choose>
  415.             </xsl:otherwise>
  416.         </xsl:choose>
  417.         <mml:mrow>
  418.             <xsl:apply-templates select="m:e[1]" />
  419.         </mml:mrow>
  420.     </xsl:template>
  421.     
  422.     <xsl:template match="m:limLow">
  423.         <mml:munder>
  424.             <mml:mrow>
  425.                 <xsl:apply-templates select="m:e[1]" />
  426.             </mml:mrow>
  427.             <mml:mrow>
  428.                 <xsl:apply-templates select="m:lim[1]" />
  429.             </mml:mrow>
  430.         </mml:munder>
  431.     </xsl:template>
  432.     
  433.     <xsl:template match="m:limUpp">
  434.         <mml:mover>
  435.             <mml:mrow>
  436.                 <xsl:apply-templates select="m:e[1]" />
  437.             </mml:mrow>
  438.             <mml:mrow>
  439.                 <xsl:apply-templates select="m:lim[1]" />
  440.             </mml:mrow>
  441.         </mml:mover>
  442.     </xsl:template>
  443.     
  444.     <xsl:template match="m:sSub">
  445.         <mml:msub>
  446.             <mml:mrow>
  447.                 <xsl:apply-templates select="m:e[1]" />
  448.             </mml:mrow>
  449.             <mml:mrow>
  450.                 <xsl:apply-templates select="m:sub[1]" />
  451.             </mml:mrow>
  452.         </mml:msub>        
  453.     </xsl:template>
  454.     
  455.     <xsl:template match="m:sSup">
  456.         <mml:msup>
  457.             <mml:mrow>
  458.                 <xsl:apply-templates select="m:e[1]" />
  459.             </mml:mrow>
  460.             <mml:mrow>
  461.                 <xsl:apply-templates select="m:sup[1]" />
  462.             </mml:mrow>
  463.         </mml:msup>        
  464.     </xsl:template>
  465.     
  466.     <xsl:template match="m:sSubSup">
  467.         <mml:msubsup>
  468.             <mml:mrow>
  469.                 <xsl:apply-templates select="m:e[1]" />
  470.             </mml:mrow>
  471.             <mml:mrow>
  472.                 <xsl:apply-templates select="m:sub[1]" />
  473.             </mml:mrow>
  474.             <mml:mrow>
  475.                 <xsl:apply-templates select="m:sup[1]" />
  476.             </mml:mrow>
  477.         </mml:msubsup>
  478.     </xsl:template>
  479.             
  480.     <xsl:template match="m:groupChr">
  481.         <xsl:variable name="sLowerCaseOpEmu" select="translate(m:groupChrPr[last()]/m:opEmu/@m:val, 
  482.                                                            'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
  483.                                                            'abcdefghijklmnopqrstuvwxyz')" />
  484.         <xsl:choose>
  485.             <xsl:when test="$sLowerCaseOpEmu='on'">
  486.                 <mml:mrow>
  487.                     <xsl:call-template name="CreateGroupChr" />
  488.                 </mml:mrow>
  489.             </xsl:when>
  490.             <xsl:otherwise>
  491.                 <xsl:call-template name="CreateGroupChr" />
  492.             </xsl:otherwise>
  493.         </xsl:choose>
  494.     </xsl:template>
  495.     
  496.     <xsl:template name="CreateGroupChr">
  497.         <xsl:variable name="sLowerCasePos" select="translate(m:groupChrPr[last()]/m:pos/@m:val, 
  498.                                                              'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
  499.                                                              'abcdefghijklmnopqrstuvwxyz')" />
  500.         <xsl:choose>
  501.             <xsl:when test="$sLowerCasePos!='top' or 
  502.                         not(m:groupChrPr[last()]/m:pos/@m:val)   or
  503.                         m:groupChrPr[last()]/m:pos/@m:val=''">
  504.                 <mml:munder>
  505.                     <xsl:apply-templates select="m:e[1]" />
  506.                     <mml:mo>
  507.                         <xsl:choose>
  508.                             <xsl:when test="string-length(m:groupChrPr[last()]/m:chr/@m:val) >= 1">
  509.                                 <xsl:value-of select="substring(m:groupChrPr[last()]/m:chr/@m:val,1,1)" />
  510.                             </xsl:when>
  511.                             <xsl:otherwise>
  512.                                 <xsl:text disable-output-escaping="yes">&#x023DF;</xsl:text>
  513.                             </xsl:otherwise>
  514.                         </xsl:choose>
  515.                     </mml:mo>
  516.                 </mml:munder>
  517.             </xsl:when>
  518.             <xsl:otherwise>
  519.                 <mml:mover>
  520.                     <xsl:apply-templates select="m:e[1]" />
  521.                     <mml:mo>
  522.                         <xsl:choose>
  523.                             <xsl:when test="string-length(m:groupChrPr[last()]/m:chr/@m:val) >= 1">
  524.                                 <xsl:value-of select="substring(m:groupChrPr[last()]/m:chr/@m:val,1,1)" />
  525.                             </xsl:when>
  526.                             <xsl:otherwise>
  527.                                 <xsl:text disable-output-escaping="yes">&#x023DF;</xsl:text>
  528.                             </xsl:otherwise>
  529.                         </xsl:choose>
  530.                     </mml:mo>
  531.                 </mml:mover>
  532.             </xsl:otherwise>
  533.         </xsl:choose>
  534.     </xsl:template>
  535.     
  536.     <xsl:template name="fName">
  537.         <xsl:for-each select="m:fName/*">
  538.             <xsl:apply-templates select="." />
  539.         </xsl:for-each>
  540.     </xsl:template>
  541.     
  542.     <xsl:template match="m:func">
  543.         <mml:mrow>
  544.             <mml:mrow>
  545.                 <xsl:call-template name="fName" />
  546.             </mml:mrow>
  547.             <mml:mo>⁡</mml:mo>
  548.             <xsl:apply-templates select="*[position() > 1]" />
  549.         </mml:mrow>
  550.     </xsl:template>
  551.  
  552.     <!-- %%Template: match m:f 
  553.         
  554.         m:f maps directly to mfrac. 
  555.     -->
  556.     <xsl:template match="m:f">
  557.         <mml:mfrac>
  558.             <xsl:call-template name="CreateMathMLFracProp">
  559.                 <xsl:with-param name="type" select="m:fPr[last()]/m:type/@m:val" />
  560.                 <xsl:with-param name="baseJc" select="m:fPr[last()]/m:baseJc/@m:val" />
  561.                 <xsl:with-param name="numJc" select="m:fPr[last()]/m:numJc/@m:val" />
  562.                 <xsl:with-param name="denJc" select="m:fPr[last()]/m:type/@m:val" />
  563.             </xsl:call-template>
  564.             
  565.             <mml:mrow><xsl:apply-templates select="m:num[1]" /></mml:mrow>
  566.             <mml:mrow><xsl:apply-templates select="m:den[1]" /></mml:mrow>
  567.         </mml:mfrac>
  568.     </xsl:template>
  569.  
  570.  
  571.     <!-- %%Template: CreateMathMLFracProp 
  572.         
  573.             Make fraction properties based on supplied parameters.
  574.             OMML differentiates between a linear fraction and a skewed
  575.             one. For MathML, we write both as bevelled.
  576.     -->
  577.     <xsl:template name="CreateMathMLFracProp">
  578.         <xsl:param name="type" />
  579.         <xsl:param name="baseJc" />
  580.         <xsl:param name="numJc" />
  581.         <xsl:param name="denJc" />
  582.         <xsl:variable name="sLowerCaseType" select="translate($type, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')" />
  583.         <xsl:variable name="sLowerCaseNumJc" select="translate($numJc, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')" />
  584.         <xsl:variable name="sLowerCaseDenJc" select="translate($denJc, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')" />
  585.         
  586.         <xsl:if test="$sLowerCaseType='skw' or $sLowerCaseType='lin'">
  587.             <xsl:attribute name="bevelled">true</xsl:attribute>
  588.         </xsl:if>
  589.         <xsl:if test="$sLowerCaseType='nobar'">
  590.             <xsl:attribute name="linethickness">0pt</xsl:attribute>
  591.         </xsl:if>
  592.         <xsl:choose>
  593.             <xsl:when test="sLowerCaseNumJc='right'">
  594.                 <xsl:attribute name="numalign">right</xsl:attribute>
  595.             </xsl:when>
  596.             <xsl:when test="sLowerCaseNumJc='left'">
  597.                 <xsl:attribute name="numalign">left</xsl:attribute>
  598.             </xsl:when>
  599.         </xsl:choose>
  600.         <xsl:choose>
  601.             <xsl:when test="sLowerCaseDenJc='right'">
  602.                 <xsl:attribute name="numalign">right</xsl:attribute>
  603.             </xsl:when>
  604.             <xsl:when test="sLowerCaseDenJc='left'">
  605.                 <xsl:attribute name="numalign">left</xsl:attribute>
  606.             </xsl:when>
  607.         </xsl:choose>
  608.     </xsl:template>
  609.  
  610.     <!-- %%Template: match m:e | m:den | m:num | m:lim | m:sup | m:sub 
  611.         
  612.         These element delinate parts of an equation (like the numerator).  -->
  613.     <xsl:template match="m:e | m:den | m:num | m:lim | m:sup | m:sub">
  614.         <xsl:choose>
  615.  
  616.             <!-- If there is no scriptLevel speified, just call through -->
  617.             <xsl:when test="not(m:argPr[last()]/m:scrLvl/@m:val)">
  618.                 <xsl:apply-templates select="*" />
  619.             </xsl:when>
  620.  
  621.             <!-- Otherwise, create an mstyle and set the script level -->
  622.             <xsl:otherwise>
  623.                 <mml:mstyle>
  624.                     <xsl:attribute name="scriptlevel">
  625.                         <xsl:value-of select="m:argPr[last()]/m:scrLvl/@m:val" />
  626.                     </xsl:attribute>
  627.                     <xsl:apply-templates select="*" />
  628.                 </mml:mstyle>
  629.             </xsl:otherwise>
  630.         </xsl:choose>
  631.     </xsl:template>
  632.         
  633.     <xsl:template match="m:bar">
  634.         <xsl:variable name="sLowerCasePos" select="translate(m:barPr/m:pos/@m:val, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
  635.                                                                                'abcdefghijklmnopqrstuvwxyz')" />
  636.         <xsl:choose>
  637.             <xsl:when test="$sLowerCasePos!='bot' or 
  638.                             not($sLowerCasePos)   or
  639.                             $sLowerCasePos=''   ">
  640.                 <mml:mover>
  641.                     <xsl:attribute name="accent">true</xsl:attribute>
  642.                     <xsl:apply-templates select="m:e[1]" />
  643.                     <mml:mo>
  644.                         <xsl:text disable-output-escaping="yes">&#x000AF;</xsl:text>
  645.                     </mml:mo>
  646.                 </mml:mover>
  647.             </xsl:when>
  648.             <xsl:otherwise>
  649.                 <mml:munder>
  650.                     <xsl:apply-templates select="m:e[1]" />
  651.                     <mml:mo>
  652.                         <xsl:text disable-output-escaping="yes">&#x00332;</xsl:text>
  653.                     </mml:mo>
  654.                 </mml:munder>
  655.             </xsl:otherwise>
  656.         </xsl:choose>
  657.     </xsl:template>
  658.     
  659.     <!-- %%Template match m:d
  660.  
  661.         Process a delimiter. 
  662.     -->
  663.     <xsl:template match="m:d">
  664.         <mml:mfenced>        
  665.             <!-- open: default is ( for both OMML and MathML -->
  666.             <xsl:if test="m:dPr[1]/m:begChr/@m:val and not(m:dPr[1]/m:begChr/@m:val ='(')">
  667.                 <xsl:attribute name="open">
  668.                     <xsl:value-of select="m:dPr[1]/m:begChr/@m:val" />
  669.                 </xsl:attribute>
  670.             </xsl:if>
  671.             
  672.             <!-- close: default is ) for both OMML and MathML -->
  673.             <xsl:if test="m:dPr[1]/m:endChr/@m:val and not(m:dPr[1]/m:endChr/@m:val =')')">
  674.                 <xsl:attribute name="close">
  675.                     <xsl:value-of select="m:dPr[1]/m:endChr/@m:val" />
  676.                 </xsl:attribute>
  677.             </xsl:if>
  678.             
  679.             <!-- separator: the default is ',' for MathML, and '|' for OMML -->
  680.             <xsl:choose>
  681.                 <!-- Matches MathML default. Write nothing -->
  682.                 <xsl:when test="m:dPr[1]/m:sepChr/@m:val = ','" />
  683.  
  684.                 <!-- OMML default: | -->
  685.                 <xsl:when test="not(m:dPr[1]/m:sepChr/@m:val)">
  686.                     <xsl:attribute name="separators">
  687.                         <xsl:value-of select="'|'" />
  688.                     </xsl:attribute>
  689.                 </xsl:when>
  690.  
  691.                 <xsl:otherwise>
  692.                     <xsl:attribute name="separators">
  693.                         <xsl:value-of select="m:dPr[1]/m:sepChr/@m:val" />
  694.                     </xsl:attribute>            
  695.                 </xsl:otherwise>                        
  696.             </xsl:choose>
  697.             
  698.             <!-- now write all the children. Put each one into an mrow
  699.             just in case it produces multiple runs, etc -->
  700.             <xsl:for-each select="m:e">
  701.                 <mml:mrow>
  702.                     <xsl:apply-templates select="." />
  703.                 </mml:mrow>
  704.             </xsl:for-each>            
  705.         </mml:mfenced>
  706.     </xsl:template>
  707.     
  708.     <xsl:template match="m:r">
  709.         <xsl:variable name="sLowerCaseNor" select="translate(child::m:rPr[last()]/m:nor/@m:val, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
  710.                                                                                'abcdefghijklmnopqrstuvwxyz')" />
  711.         <xsl:choose>
  712.             <xsl:when test="$sLowerCaseNor='on'">
  713.                 <mml:mtext>
  714.                     <xsl:value-of select=".//m:t" />
  715.                 </mml:mtext>
  716.             </xsl:when>
  717.             <xsl:otherwise>
  718.                 <xsl:for-each select=".//m:t">
  719.                     <xsl:call-template name="ParseMt">
  720.                         <xsl:with-param name="sToParse" select="text()" />
  721.                         <xsl:with-param name="scr" select="../m:rPr[last()]/m:scr/@m:val" />
  722.                         <xsl:with-param name="sty" select="../m:rPr[last()]/m:sty/@m:val" />
  723.                         <xsl:with-param name="nor" select="../m:rPr[last()]/m:nor/@m:val" />
  724.                     </xsl:call-template>
  725.                 </xsl:for-each>
  726.             </xsl:otherwise>
  727.         </xsl:choose>
  728.     </xsl:template>
  729.     
  730.     <xsl:template name="CreateTokenAttributes">
  731.         <xsl:param name="scr" />
  732.         <xsl:param name="sty" />
  733.         <xsl:param name="nor" />
  734.         <xsl:param name="nCharToPrint" />
  735.         <xsl:param name="sTokenType" />
  736.         <xsl:variable name="sLowerCaseNor" select="translate($nor, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
  737.                                                                    'abcdefghijklmnopqrstuvwxyz')" />
  738.         <xsl:choose>
  739.             <xsl:when test="$sLowerCaseNor = 'on'">
  740.                 <xsl:attribute name="mathvariant">normal</xsl:attribute>
  741.             </xsl:when>
  742.             <xsl:otherwise>
  743.                 <xsl:variable name="mathvariant">
  744.                     <xsl:choose>
  745.                         <!-- numbers don't care -->
  746.                         <xsl:when test="$sTokenType='mn'" />
  747.  
  748.                         <xsl:when test="$scr='monospace'">monospace</xsl:when>
  749.                         <xsl:when test="$scr='sans-serif' and $sty='i'">sans-serif-italic</xsl:when>
  750.                         <xsl:when test="$scr='sans-serif' and $sty='b'">bold-sans-serif</xsl:when>
  751.                         <xsl:when test="$scr='sans-serif'">sans-serif</xsl:when>
  752.                         <xsl:when test="$scr='fraktur' and $sty='b'">bold-fraktur</xsl:when>
  753.                         <xsl:when test="$scr='fraktur'">fraktur</xsl:when>
  754.                         <xsl:when test="$scr='double-struck'">double-struck</xsl:when>
  755.                         <xsl:when test="$scr='script' and $sty='b'">bold-script</xsl:when>
  756.                         <xsl:when test="$scr='script'">script</xsl:when>
  757.                         <xsl:when test="($scr='roman' or not($scr) or $scr='') and $sty='b'">bold</xsl:when>
  758.                         <xsl:when test="($scr='roman' or not($scr) or $scr='') and $sty='i'">italic</xsl:when>
  759.                         <xsl:when test="($scr='roman' or not($scr) or $scr='') and $sty='p'">normal</xsl:when>
  760.                         
  761.                         <xsl:otherwise />
  762.                     </xsl:choose>
  763.                 </xsl:variable>
  764.                 <xsl:variable name="fontweight">
  765.                     <xsl:choose>
  766.                         <xsl:when test="$sty='b' or $sty='bi'">bold</xsl:when>
  767.                         <xsl:otherwise>normal</xsl:otherwise>
  768.                     </xsl:choose>
  769.                 </xsl:variable>
  770.                 <xsl:variable name="fontstyle">
  771.                     <xsl:choose>
  772.                         <xsl:when test="$sty='p' or $sty='b'">normal</xsl:when>
  773.                         <xsl:otherwise>italic</xsl:otherwise>
  774.                     </xsl:choose>
  775.                 </xsl:variable>
  776.  
  777.                 <!-- Writing of attributes begins here -->
  778.                 <xsl:choose>
  779.                     <!-- Don't write mathvariant for operators unless they want to be normal -->
  780.                     <xsl:when test="$sTokenType='mo' and $mathvariant!='normal'" />
  781.  
  782.                     <!-- A single character within an mi is already italics, don't write -->
  783.                     <xsl:when test="$sTokenType='mi' and $nCharToPrint=1 and ($mathvariant='' or $mathvariant='italic')" />
  784.                     
  785.                     <xsl:when test="$sTokenType='mi' and $nCharToPrint > 1 and ($mathvariant='' or $mathvariant='italic')">
  786.                         <xsl:attribute name="mathvariant">
  787.                             <xsl:value-of select="'italic'" />
  788.                         </xsl:attribute>
  789.                     </xsl:when>
  790.                     <xsl:when test="$mathvariant!='italic' and $mathvariant!=''">
  791.                         <xsl:attribute name="mathvariant">
  792.                             <xsl:value-of select="$mathvariant" />
  793.                         </xsl:attribute>
  794.                     </xsl:when>
  795.                     <xsl:otherwise>
  796.                         <xsl:if test="not($sTokenType='mi' and $nCharToPrint=1) and $fontstyle='italic'">
  797.                             <xsl:attribute name="fontstyle">italic</xsl:attribute>
  798.                         </xsl:if>
  799.                         <xsl:if test="$fontweight='bold'">
  800.                             <xsl:attribute name="fontweight">bold</xsl:attribute>
  801.                         </xsl:if>
  802.                     </xsl:otherwise>
  803.                 </xsl:choose>
  804.             </xsl:otherwise>
  805.         </xsl:choose>
  806.     </xsl:template>
  807.     
  808.     <xsl:template match="m:eqArr">
  809.         <mml:mtable>
  810.             <xsl:attribute name="frame">none</xsl:attribute>
  811.             <xsl:attribute name="columnlines">none</xsl:attribute>
  812.             <xsl:attribute name="rowlines">none</xsl:attribute>
  813.             <xsl:for-each select="m:e">
  814.                 <mml:mtr>
  815.                     <mml:mtd>
  816.                         <mml:maligngroup />
  817.                         <xsl:choose>
  818.                             <xsl:when test="m:argPr[last()]/m:scrLvl/@m:val!='0' or 
  819.                                 not(m:argPr[last()]/m:scrLvl/@m:val)  or 
  820.                                 m:argPr[last()]/m:scrLvl/@m:val=''">
  821.                                 <mml:mrow>
  822.                                     <xsl:call-template name="CreateEqArrRow">
  823.                                         <xsl:with-param name="align" select="1" />
  824.                                         <xsl:with-param name="ndCur" select="*[1]" />
  825.                                     </xsl:call-template>
  826.                                 </mml:mrow>
  827.                             </xsl:when>
  828.                             <xsl:otherwise>
  829.                                 <mml:mstyle>
  830.                                     <xsl:attribute name="scriptlevel">
  831.                                         <xsl:value-of select="m:argPr[last()]/m:scrLvl/@m:val" />
  832.                                     </xsl:attribute>
  833.                                     <xsl:call-template name="CreateEqArrRow">
  834.                                         <xsl:with-param name="align" select="1" />
  835.                                         <xsl:with-param name="ndCur" select="*[1]" />
  836.                                     </xsl:call-template>
  837.                                 </mml:mstyle>
  838.                             </xsl:otherwise>
  839.                         </xsl:choose>
  840.                     </mml:mtd>
  841.                 </mml:mtr>
  842.             </xsl:for-each>
  843.         </mml:mtable>
  844.     </xsl:template>
  845.     
  846.     <xsl:template name="CreateEqArrRow">
  847.         <xsl:param name="align" /> 
  848.         <xsl:param name="ndCur" />
  849.         <xsl:variable name="sAllMt"> 
  850.             <xsl:for-each select="$ndCur/m:t">
  851.                 <xsl:value-of select="." />
  852.             </xsl:for-each>
  853.         </xsl:variable>
  854.         <xsl:choose>
  855.             <xsl:when test="local-name($ndCur)='r' and
  856.                             namespace-uri($ndCur)='http://schemas.microsoft.com/office/omml/2004/12/core'">
  857.  
  858.                 <xsl:call-template name="ParseEqArrMr">
  859.                     <xsl:with-param name="sToParse" select="$sAllMt" />
  860.                     <xsl:with-param name="scr" select="../m:rPr[last()]/m:scr/@m:val" />
  861.                     <xsl:with-param name="sty" select="../m:rPr[last()]/m:sty/@m:val" />
  862.                     <xsl:with-param name="nor" select="../m:rPr[last()]/m:nor/@m:val" />
  863.                     <xsl:with-param name="align" select="$align" />
  864.                 </xsl:call-template>
  865.             </xsl:when>
  866.             <xsl:otherwise>
  867.                 <xsl:apply-templates select="$ndCur" />
  868.             </xsl:otherwise>
  869.         </xsl:choose>
  870.         <xsl:if test="count($ndCur/following-sibling::*) > 0">
  871.             <xsl:variable name="cAmp">
  872.                 <xsl:call-template name="CountAmp">
  873.                     <xsl:with-param name="sAllMt" select="$sAllMt" />
  874.                     <xsl:with-param name="cAmp" select="0" />
  875.                 </xsl:call-template>
  876.             </xsl:variable>
  877.             <xsl:call-template name="CreateEqArrRow">
  878.                 <xsl:with-param name="align" select="($align+($cAmp mod 2)) mod 2" />
  879.                 <xsl:with-param name="ndCur" select="$ndCur/following-sibling::*[1]" />
  880.             </xsl:call-template>
  881.         </xsl:if>
  882.     </xsl:template>
  883.     
  884.     <xsl:template name="CountAmp">
  885.         <xsl:param name="sAllMt" />
  886.         <xsl:param name="cAmp" />
  887.         <xsl:choose>
  888.             <xsl:when test="string-length(substring-after($sAllMt, '&')) > 0 or 
  889.                             substring($sAllMt, string-length($sAllMt))='&'">
  890.                 <xsl:call-template name="CountAmp">
  891.                     <xsl:with-param name="sAllMt" select="substring-after($sAllMt, '&')" />
  892.                     <xsl:with-param name="cAmp" select="$cAmp+1" />
  893.                 </xsl:call-template>
  894.             </xsl:when>
  895.             <xsl:otherwise>
  896.                 <xsl:value-of select="$cAmp" />
  897.             </xsl:otherwise>
  898.         </xsl:choose>
  899.     </xsl:template>
  900.     
  901.     <!-- %%Template: ParseEqArrMr
  902.             
  903.             Similar to ParseMt, but this one has to do more for an equation 
  904.             array. The presence of & in a run that is in an equation array
  905.             indicates alignment 
  906.     -->
  907.     <xsl:template name="ParseEqArrMr">
  908.         <xsl:param name="sToParse" />
  909.         <xsl:param name="sty" />
  910.         <xsl:param name="scr" />
  911.         <xsl:param name="nor" />
  912.         <xsl:param name="align" />
  913.         <xsl:if test="string-length($sToParse) > 0"> 
  914.             <xsl:choose>
  915.                 <xsl:when test="substring($sToParse,1,1) = '&'">
  916.                     <xsl:choose>
  917.                         <xsl:when test="$align='0'">
  918.                             <mml:maligngroup />
  919.                         </xsl:when>
  920.                         <xsl:when test="$align='1'">
  921.                             <mml:malignmark>
  922.                                 <xsl:attribute name="edge">left</xsl:attribute>
  923.                             </mml:malignmark>
  924.                         </xsl:when>
  925.                     </xsl:choose>
  926.                     <xsl:call-template name="ParseEqArrMr">
  927.                         <xsl:with-param name="sToParse" select="substring($sToParse,2)" />
  928.                         <xsl:with-param name="scr" select="$scr" />
  929.                         <xsl:with-param name="sty" select="$sty" />
  930.                         <xsl:with-param name="nor" select="$nor" />
  931.                         <xsl:with-param name="align">
  932.                             <xsl:choose>
  933.                                 <xsl:when test="$align='1'">0</xsl:when>
  934.                                 <xsl:otherwise>1</xsl:otherwise>
  935.                             </xsl:choose>
  936.                         </xsl:with-param>
  937.                     </xsl:call-template>
  938.                 </xsl:when>
  939.                 <xsl:otherwise> 
  940.                     <xsl:variable name="sRepNumWith0">
  941.                         <xsl:call-template name="SReplaceNumWithZero">
  942.                             <xsl:with-param name="sToParse" select="$sToParse" />
  943.                         </xsl:call-template>
  944.                     </xsl:variable>                    
  945.                     <xsl:variable name="sRepOperWith-">
  946.                         <xsl:call-template name="SReplaceOperWithMinus">
  947.                             <xsl:with-param name="sToParse" select="$sRepNumWith0" />
  948.                         </xsl:call-template>
  949.                     </xsl:variable>
  950.                     
  951.                     <xsl:variable name="iFirstOper" select="string-length($sRepOperWith-) - string-length(substring-after($sRepOperWith-, '-'))" />
  952.                     <xsl:variable name="iFirstNum" select="string-length($sRepOperWith-) - string-length(substring-after($sRepOperWith-, '0'))" />
  953.                     <xsl:variable name="iFirstAmp" select="string-length($sRepOperWith-) - string-length(substring-after($sRepOperWith-, '&'))" />
  954.                     <xsl:variable name="fNumAtPos1"> 
  955.                         <xsl:choose>
  956.                             <xsl:when test="substring($sRepOperWith-,1,1)='0'">1</xsl:when>
  957.                             <xsl:otherwise>0</xsl:otherwise>
  958.                         </xsl:choose>
  959.                     </xsl:variable>
  960.                     <xsl:variable name="fOperAtPos1"> 
  961.                         <xsl:choose>
  962.                             <xsl:when test="substring($sRepOperWith-,1,1)='-'">1</xsl:when>
  963.                             <xsl:otherwise>0</xsl:otherwise>
  964.                         </xsl:choose>
  965.                     </xsl:variable>
  966.                     <xsl:choose>
  967.                     
  968.                         <!-- Case I: The string begins with neither a number, nor an operator -->
  969.                         <xsl:when test="$fNumAtPos1='0' and $fOperAtPos1='0'">
  970.                             <xsl:variable name="nCharToPrint">
  971.                                 <xsl:choose>
  972.                                     <xsl:when test="($iFirstOper=$iFirstNum) and 
  973.                                                 ($iFirstAmp=$iFirstOper) and
  974.                                                 ($iFirstOper=string-length($sToParse)) and
  975.                                                 $fNumAtPos1='0' and
  976.                                                 $fOperAtPos1='0'">
  977.                                         <xsl:value-of select="string-length($sToParse)" />
  978.                                     </xsl:when>
  979.                                     <xsl:when test="($iFirstOper < $iFirstNum) and 
  980.                                                 ($iFirstOper < $iFirstAmp)">
  981.                                         <xsl:value-of select="$iFirstOper - 1" />
  982.                                     </xsl:when>
  983.                                     <xsl:when test="($iFirstNum < $iFirstOper) and 
  984.                                                 ($iFirstNum < $iFirstAmp)">
  985.                                         <xsl:value-of select="$iFirstNum - 1" />
  986.                                     </xsl:when>
  987.                                     <xsl:otherwise>
  988.                                         <xsl:value-of select="$iFirstAmp - 1" />
  989.                                     </xsl:otherwise>
  990.                                 </xsl:choose>
  991.                             </xsl:variable>
  992.                             <mml:mi>
  993.                                 <xsl:call-template name="CreateTokenAttributes">
  994.                                     <xsl:with-param name="scr" select="$scr" />
  995.                                     <xsl:with-param name="sty" select="$sty" />
  996.                                     <xsl:with-param name="nor" select="$nor" />
  997.                                     <xsl:with-param name="nCharToPrint" select="$nCharToPrint" />
  998.                                     <xsl:with-param name="sTokenType" select="'mi'" />
  999.                                 </xsl:call-template>
  1000.                                 <xsl:value-of select="substring($sToParse,1,$nCharToPrint)" />
  1001.                             </mml:mi>
  1002.                             <xsl:call-template name="ParseEqArrMr">
  1003.                                 <xsl:with-param name="sToParse" select="substring($sToParse, $nCharToPrint+1)" />
  1004.                                 <xsl:with-param name="scr" select="$scr" />
  1005.                                 <xsl:with-param name="sty" select="$sty" />
  1006.                                 <xsl:with-param name="nor" select="$nor" />
  1007.                                 <xsl:with-param name="align" select="$align" />
  1008.                             </xsl:call-template>
  1009.                         </xsl:when>
  1010.                         
  1011.                         <!-- Case II: There is an operator at position 1 -->
  1012.                         <xsl:when test="$fOperAtPos1='1'">
  1013.                             <mml:mo>
  1014.                                 <xsl:call-template name="CreateTokenAttributes">
  1015.                                     <xsl:with-param name="scr" />
  1016.                                     <xsl:with-param name="sty" />
  1017.                                     <xsl:with-param name="nor" select="$nor" />
  1018.                                     <xsl:with-param name="sTokenType" select="'mo'" />
  1019.                                 </xsl:call-template>
  1020.                                 <xsl:value-of select="substring($sToParse,1,1)" />
  1021.                             </mml:mo>
  1022.                             <xsl:call-template name="ParseEqArrMr">
  1023.                                 <xsl:with-param name="sToParse" select="substring($sToParse, 2)" />
  1024.                                 <xsl:with-param name="scr" select="$scr" />
  1025.                                 <xsl:with-param name="sty" select="$sty" />
  1026.                                 <xsl:with-param name="nor" select="$nor" />
  1027.                                 <xsl:with-param name="align" select="$align" />
  1028.                             </xsl:call-template>
  1029.                         </xsl:when>
  1030.                         
  1031.                         <!-- Case III: There is a number at position 1 -->
  1032.                         <xsl:otherwise>
  1033.                             <xsl:variable name="sConsecNum">
  1034.                                 <xsl:call-template name="SNumStart">
  1035.                                     <xsl:with-param name="sToParse" select="$sToParse" />
  1036.                                     <xsl:with-param name="sPattern" select="$sRepNumWith0" />                                    
  1037.                                 </xsl:call-template>
  1038.                             </xsl:variable>
  1039.                             <mml:mn>
  1040.                                 <xsl:call-template name="CreateTokenAttributes">
  1041.                                     <xsl:with-param name="scr" />
  1042.                                     <xsl:with-param name="sty" />
  1043.                                     <xsl:with-param name="nor" select="$nor" />
  1044.                                     <xsl:with-param name="sTokenType" select="'mn'" />
  1045.                                 </xsl:call-template>
  1046.                                 <xsl:value-of select="$sConsecNum" />
  1047.                             </mml:mn>
  1048.                             <xsl:call-template name="ParseEqArrMr">
  1049.                                 <xsl:with-param name="sToParse" select="substring-after($sToParse, $sConsecNum)" />
  1050.                                 <xsl:with-param name="scr" select="$scr" />
  1051.                                 <xsl:with-param name="sty" select="$sty" />
  1052.                                 <xsl:with-param name="nor" select="$nor" />
  1053.                                 <xsl:with-param name="align" select="$align" />
  1054.                             </xsl:call-template>
  1055.                         </xsl:otherwise>
  1056.                     </xsl:choose>
  1057.                 </xsl:otherwise>
  1058.             </xsl:choose>
  1059.         </xsl:if>
  1060.     </xsl:template>
  1061.     
  1062.     <!-- %%Template: ParseMt
  1063.  
  1064.             Produce a run of text. Technically, OMML makes no distinction 
  1065.             between numbers, operators, and other characters in a run. For 
  1066.             MathML we need to break these into mi, mn, or mo elements. 
  1067.             
  1068.             See also ParseEqArrMr
  1069.     -->    
  1070.     <xsl:template name="ParseMt">
  1071.         <xsl:param name="sToParse" />
  1072.         <xsl:param name="sty" />
  1073.         <xsl:param name="scr" />
  1074.         <xsl:param name="nor" />
  1075.         <xsl:if test="string-length($sToParse) > 0">        
  1076.             <xsl:variable name="sRepNumWith0">
  1077.                 <xsl:call-template name="SReplaceNumWithZero">
  1078.                     <xsl:with-param name="sToParse" select="$sToParse" />
  1079.                 </xsl:call-template>
  1080.             </xsl:variable>
  1081.             <xsl:variable name="sRepOperWith-">
  1082.                 <xsl:call-template name="SReplaceOperWithMinus">
  1083.                     <xsl:with-param name="sToParse" select="$sRepNumWith0" />
  1084.                 </xsl:call-template>
  1085.             </xsl:variable>
  1086.             
  1087.             <xsl:variable name="iFirstOper" select="string-length($sRepOperWith-) - string-length(substring-after($sRepOperWith-, '-'))" />
  1088.             <xsl:variable name="iFirstNum" select="string-length($sRepOperWith-) - string-length(substring-after($sRepOperWith-, '0'))" />
  1089.             <xsl:variable name="fNumAtPos1">
  1090.                 <xsl:choose>
  1091.                     <xsl:when test="substring($sRepOperWith-,1,1)='0'">1</xsl:when>
  1092.                     <xsl:otherwise>0</xsl:otherwise>
  1093.                 </xsl:choose>
  1094.             </xsl:variable>
  1095.             <xsl:variable name="fOperAtPos1"> 
  1096.                 <xsl:choose>
  1097.                     <xsl:when test="substring($sRepOperWith-,1,1)='-'">1</xsl:when>
  1098.                     <xsl:otherwise>0</xsl:otherwise>
  1099.                 </xsl:choose>
  1100.             </xsl:variable>
  1101.             
  1102.             <xsl:choose>
  1103.                 
  1104.                 <!-- Case I: The string begins with neither a number, nor an operator -->
  1105.                 <xsl:when test="$fOperAtPos1='0' and $fNumAtPos1='0'">
  1106.                     <xsl:variable name="nCharToPrint">
  1107.                         <xsl:choose>
  1108.                             <xsl:when test="($iFirstOper=$iFirstNum) and 
  1109.                                             ($iFirstOper=string-length($sToParse)) and
  1110.                                             (substring($sRepOperWith-, string-length($sRepOperWith-))!='0') and 
  1111.                                             (substring($sRepOperWith-, string-length($sRepOperWith-))!='-')">
  1112.                                 <xsl:value-of select="string-length($sToParse)" />
  1113.                             </xsl:when>
  1114.                             <xsl:when test="$iFirstOper < $iFirstNum">
  1115.                                 <xsl:value-of select="$iFirstOper - 1" />
  1116.                             </xsl:when>
  1117.                             <xsl:otherwise>
  1118.                                 <xsl:value-of select="$iFirstNum - 1" />
  1119.                             </xsl:otherwise>
  1120.                         </xsl:choose>
  1121.                     </xsl:variable>
  1122.                     <mml:mi>
  1123.                         <xsl:call-template name="CreateTokenAttributes">
  1124.                             <xsl:with-param name="scr" select="$scr" />
  1125.                             <xsl:with-param name="sty" select="$sty" />
  1126.                             <xsl:with-param name="nor" select="$nor" />
  1127.                             <xsl:with-param name="nCharToPrint" select="$nCharToPrint" />
  1128.                             <xsl:with-param name="sTokenType" select="'mi'" />
  1129.                         </xsl:call-template>
  1130.                         <xsl:value-of select="substring($sToParse,1,$nCharToPrint)" />
  1131.                     </mml:mi>
  1132.                     <xsl:call-template name="ParseMt">
  1133.                         <xsl:with-param name="sToParse" select="substring($sToParse, $nCharToPrint+1)" />
  1134.                         <xsl:with-param name="scr" select="$scr" />
  1135.                         <xsl:with-param name="sty" select="$sty" />
  1136.                         <xsl:with-param name="nor" select="$nor" />
  1137.                     </xsl:call-template>
  1138.                 </xsl:when>
  1139.                 
  1140.                 <!-- Case II: There is an operator at position 1 -->
  1141.                 <xsl:when test="$fOperAtPos1='1'">
  1142.                     <mml:mo>
  1143.                         <xsl:call-template name="CreateTokenAttributes">
  1144.                             <xsl:with-param name="scr" />
  1145.                             <xsl:with-param name="sty" />
  1146.                             <xsl:with-param name="nor" select="$nor" />
  1147.                             <xsl:with-param name="sTokenType" select="'mo'" />
  1148.                         </xsl:call-template>
  1149.                         <xsl:value-of select="substring($sToParse,1,1)" />
  1150.                     </mml:mo>
  1151.                     <xsl:call-template name="ParseMt">
  1152.                         <xsl:with-param name="sToParse" select="substring($sToParse, 2)" />
  1153.                         <xsl:with-param name="scr" select="$scr" />
  1154.                         <xsl:with-param name="sty" select="$sty" />
  1155.                         <xsl:with-param name="nor" select="$nor" />
  1156.                     </xsl:call-template>
  1157.                 </xsl:when>
  1158.                 
  1159.                 <!-- Case III: There is a number at position 1 -->
  1160.                 <xsl:otherwise>
  1161.                     <xsl:variable name="sConsecNum">                
  1162.                         <xsl:call-template name="SNumStart">
  1163.                             <xsl:with-param name="sToParse" select="$sToParse" />
  1164.                             <xsl:with-param name="sPattern" select="$sRepNumWith0" /> 
  1165.                         </xsl:call-template>
  1166.                     </xsl:variable>
  1167.                     <mml:mn>
  1168.                         <xsl:call-template name="CreateTokenAttributes">
  1169.                             <xsl:with-param name="scr" select="$scr" />
  1170.                             <xsl:with-param name="sty" select="'p'" />
  1171.                             <xsl:with-param name="nor" select="$nor" />
  1172.                             <xsl:with-param name="sTokenType" select="'mn'" />
  1173.                         </xsl:call-template>
  1174.                         <xsl:value-of select="$sConsecNum" />
  1175.                     </mml:mn>
  1176.                     <xsl:call-template name="ParseMt">
  1177.                         <xsl:with-param name="sToParse" select="substring-after($sToParse, $sConsecNum)" />
  1178.                         <xsl:with-param name="scr" select="$scr" />
  1179.                         <xsl:with-param name="sty" select="$sty" />
  1180.                         <xsl:with-param name="nor" select="$nor" />
  1181.                     </xsl:call-template>
  1182.                 </xsl:otherwise>
  1183.             </xsl:choose>
  1184.         </xsl:if>
  1185.     </xsl:template>
  1186.     
  1187.     <!-- %%Template: SNumStart 
  1188.     
  1189.         Return the longest substring of sToParse starting from the 
  1190.         start of sToParse that is a number. In addition, it takes the
  1191.         pattern string, which is sToParse with all of its numbers 
  1192.         replaced with a 0. sPattern should be the same length 
  1193.         as sToParse        
  1194.     -->
  1195.     <xsl:template name="SNumStart">
  1196.         <xsl:param name="sToParse" select="''" />
  1197.         <xsl:param name="sPattern" select="'$sToParse'"/>    <!-- if we don't get anything, take the string itself -->
  1198.                 
  1199.         <xsl:choose>
  1200.             <!-- the pattern says this is a number, recurse with the rest -->
  1201.             <xsl:when test="substring($sPattern, 1, 1) = '0'">
  1202.                 <xsl:call-template name="SNumStart">
  1203.                     <xsl:with-param name="sToParse" select="$sToParse" />
  1204.                     <xsl:with-param name="sPattern" select="substring($sPattern, 2)" />
  1205.                 </xsl:call-template>
  1206.             </xsl:when>
  1207.             
  1208.             <!-- the pattern says we've run out of numbers. Take as many
  1209.                 characters from sToParse as we shaved off sPattern -->
  1210.             <xsl:otherwise>
  1211.                 <xsl:value-of select="substring($sToParse, 1, string-length($sToParse) - string-length($sPattern))" />
  1212.             </xsl:otherwise>
  1213.         </xsl:choose>
  1214.     </xsl:template>
  1215.     
  1216.     <!-- %%Template SRepeatCharAcc
  1217.     
  1218.             The core of SRepeatChar with an accumulator. The current
  1219.             string is in param $acc, and we will double and recurse,
  1220.             if we're less than half of the required length or else just 
  1221.             add the right amount of characters to the accumulator and
  1222.             return
  1223.     -->
  1224.     <xsl:template name="SRepeatCharAcc">
  1225.         <xsl:param name="cchRequired" select="1" />
  1226.         <xsl:param name="ch" select="'-'" />
  1227.         <xsl:param name="acc" select="$ch" />
  1228.         
  1229.         <xsl:variable name="cchAcc" select="string-length($acc)" />
  1230.         <xsl:choose>
  1231.             <xsl:when test="(2 * $cchAcc) < $cchRequired">
  1232.                 <xsl:call-template name="SRepeatCharAcc">
  1233.                     <xsl:with-param name="cchRequired" select="$cchRequired" />
  1234.                     <xsl:with-param name="ch" select="$ch" />
  1235.                     <xsl:with-param name="acc" select="concat($acc, $acc)" />
  1236.                 </xsl:call-template>
  1237.             </xsl:when>
  1238.         
  1239.             <xsl:otherwise>
  1240.                 <xsl:value-of select="concat($acc, substring($acc, 1, $cchRequired - $cchAcc))" />
  1241.             </xsl:otherwise>
  1242.         </xsl:choose>
  1243.     </xsl:template>
  1244.     
  1245.         
  1246.     <!-- %%Template SRepeatChar
  1247.     
  1248.             Generates a string nchRequired long by repeating the given character ch
  1249.     -->
  1250.     <xsl:template name="SRepeatChar">
  1251.         <xsl:param name="cchRequired" select="1" />
  1252.         <xsl:param name="ch" select="'-'" />
  1253.         
  1254.         <xsl:call-template name="SRepeatCharAcc">
  1255.             <xsl:with-param name="cchRequired" select="$cchRequired" />
  1256.             <xsl:with-param name="ch" select="$ch" />
  1257.             <xsl:with-param name="acc" select="$ch" />
  1258.         </xsl:call-template>
  1259.     </xsl:template>
  1260.     
  1261.     <!-- %%Template SReplaceOperWithMinus
  1262.     
  1263.         Go through the given string and replace every instance
  1264.         of an operator with a minus '-'. This helps quickly identify
  1265.         the first instance of an operator.  
  1266.     -->
  1267.     <xsl:template name="SReplaceOperWithMinus">            
  1268.         <xsl:param name="sToParse" select="''" />
  1269.  
  1270.         <xsl:value-of select="translate($sToParse, $sOperators, $sMinuses)" />                                            
  1271.     </xsl:template>
  1272.     
  1273.     <!-- %%Template SReplaceNumWithZero
  1274.     
  1275.         Go through the given string and replace every instance
  1276.         of an number with a zero '0'. This helps quickly identify
  1277.         the first occurence of a number. 
  1278.         
  1279.         Considers the '.' and ',' part of a number iff they are sandwiched 
  1280.         between two other numbers. 0.3 will be recognized as a number,
  1281.         x.3 will not be. Since these characters can also be an operator, this 
  1282.         should be called before SReplaceOperWithMinus.
  1283.     -->
  1284.     <xsl:template name="SReplaceNumWithZero">            
  1285.         <xsl:param name="sToParse" select="''" />
  1286.  
  1287.         <!-- First do a simple replace. Numbers will all be come 0's.
  1288.             After this point, the pattern involving the . or , that 
  1289.             we are looking for will become 0.0 or 0,0 -->
  1290.         <xsl:variable name="sSimpleReplace" select="translate($sToParse, $sNumbers, $sZeros)" />
  1291.  
  1292.         <!-- And then, replace 0.0 with just 000. This means that the . will 
  1293.             become part of the number -->        
  1294.         <xsl:variable name="sReplacePeriod">
  1295.             <xsl:call-template name="SReplace">
  1296.                 <xsl:with-param name="sInput" select="$sSimpleReplace"/>
  1297.                 <xsl:with-param name="sOrig" select="'0.0'"/>
  1298.                 <xsl:with-param name="sReplacement" select="'000'"/>            
  1299.             </xsl:call-template>
  1300.         </xsl:variable>
  1301.         
  1302.         <!-- And then, replace 0,0 with just 000. This means that the , will 
  1303.             become part of the number -->
  1304.         <xsl:call-template name="SReplace">
  1305.             <xsl:with-param name="sInput" select="$sReplacePeriod"/>
  1306.             <xsl:with-param name="sOrig" select="'0,0'"/>
  1307.             <xsl:with-param name="sReplacement" select="'000'"/>            
  1308.         </xsl:call-template>        
  1309.     </xsl:template>    
  1310.     
  1311. </xsl:stylesheet>
  1312.